package com.zzjson.common.utils;

import com.google.common.collect.Lists;
import com.zzjson.common.utils.Pagination;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;

public class AlloyCollectionUtils {

    /**
     * 列表分组，result 可以继续 add 操作
     * @param list      待处理列表
     * @param groupSize 每组数量
     * @param <R>       返回类型
     * @return 分组执行结果列表
     */
    public static <R> List<List<R>> partition(List<R> list, int groupSize) {
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        int size = list.size();
        List<List<R>> result = new ArrayList<>(size);
        for (int i = 0; i < size; i += groupSize) {
            result.add(list.subList(i, Math.min(i + groupSize, size)));
        }
        return result;
    }

    /**
     * 分组处理并返回结果 List 集合
     * @param list      待处理列表
     * @param groupSize 每组数量
     * @param function  执行函数
     * @param <T>       入参类型
     * @param <R>       返回类型
     * @return 分组执行结果列表
     */
    public static <T, R> List<R> partitionApply(List<T> list, int groupSize, Function<List<T>, List<R>> function) {
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        int size = list.size();
        List<R> result = new ArrayList<>(size);
        for (int i = 0; i < size; i += groupSize) {
            List<R> subResult = function.apply(list.subList(i, Math.min(i + groupSize, size)));
            if (CollectionUtils.isNotEmpty(subResult)) {
                result.addAll(subResult);
            }
        }
        return result;
    }

    /**
     * 分组处理并返回结果 Map 集合
     * @param list      待处理列表
     * @param groupSize 每组数量
     * @param function  执行函数
     * @param <T>       入参类型
     * @param <K>       map -> key
     * @param <V>       map -> value
     * @return 分组执行结果列表
     */
    public static <T, K, V> Map<K, V> partitionApplyMap(List<T> list, int groupSize,
                                                        Function<List<T>, Map<K, V>> function) {
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyMap();
        }
        int size = list.size();
        Map<K, V> result = new HashMap<>(size);
        for (int i = 0; i < size; i += groupSize) {
            Map<K, V> subResult = function.apply(list.subList(i, Math.min(i + groupSize, size)));
            if (MapUtils.isNotEmpty(subResult)) {
                result.putAll(subResult);
            }
        }
        return result;
    }

    /**
     * 分组处理无结果集合
     * @param list      待处理列表
     * @param groupSize 每组数量
     * @param consumer  执行函数
     * @param <T>       入参类型
     */
    public static <T> void partitionAccept(List<T> list, int groupSize, Consumer<List<T>> consumer) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        int size = list.size();
        for (int i = 0; i < size; i += groupSize) {
            consumer.accept(list.subList(i, Math.min(i + groupSize, size)));
        }
    }

    /**
     * 分页处理并返回结果 List 集合
     * @param total    总数
     * @param pageSize 每页条数
     * @param function 执行函数
     * @param <R>      返回类型
     * @return 分页执行结果列表
     */
    public static <R> List<R> pageApply(int total, int pageSize, Function<Pagination, List<R>> function) {
        if (total <= 0) {
            return Collections.emptyList();
        }
        int pages = (total - 1) / pageSize + 1;
        Pagination page = new Pagination();
        List<R> result = new ArrayList<>(total);
        for (int pageIndex = 1; pageIndex <= pages; pageIndex++) {
            page.setPageIndex(pageIndex);
            page.setPageSize(pageSize);
            List<R> subResult = function.apply(page);
            if (CollectionUtils.isNotEmpty(subResult)) {
                result.addAll(subResult);
            }
        }
        return result;
    }

    /**
     * 分页处理无结果集合
     * @param total    总数
     * @param pageSize 每页条数
     * @param consumer 执行函数
     */
    public static void pageAccept(int total, int pageSize, Consumer<Pagination> consumer) {
        if (total <= 0) {
            return;
        }
        int pages = (total - 1) / pageSize + 1;
        Pagination page = new Pagination();
        for (int pageIndex = 1; pageIndex <= pages; pageIndex++) {
            page.setPageIndex(pageIndex);
            page.setPageSize(pageSize);
            consumer.accept(page);
        }
    }

    /**
     * 按保留首次出现元素的方式进行去重操作
     * @param list            需要去重的列表
     * @param distinctKeyFunc 去重键获取函数
     * @param <T>             泛型参数
     * @return 返回值
     */
    public static <T> List<T> keepFirstMeetElementForDistinct(List<T> list, Function<T, String> distinctKeyFunc) {
        if (CollectionUtils.isEmpty(list) || Objects.isNull(distinctKeyFunc)) {
            return list;
        }
        List<T> newList = Lists.newArrayListWithCapacity(list.size());
        List<String> distinctKeyList = Lists.newArrayListWithCapacity(list.size());
        for (T element : list) {
            String distinctKey = distinctKeyFunc.apply(element);
            if (!distinctKeyList.contains(distinctKey)) {
                newList.add(element);
                distinctKeyList.add(distinctKey);
            }
        }
        return newList;
    }

    /**
     * 计算页数
     * @param totalCount 总条数
     * @param sizeOfPage 每页大小
     * @return 返回值
     */
    public static Integer calcPages(Integer totalCount, Integer sizeOfPage) {
        if (Objects.isNull(totalCount) || totalCount <= 0) {
            return 0;
        }
        if (Objects.isNull(sizeOfPage) || sizeOfPage <= 0) {
            return 0;
        }
        return totalCount % sizeOfPage == 0 ? totalCount / sizeOfPage : totalCount / sizeOfPage + 1;
    }

    /**
     * 分割列表
     * @param originList 列表
     * @param fromIndex  开始位置
     * @param endIndex   结束位置
     * @param <T>        泛型参数
     * @return 返回值
     */
    public static <T> List<T> subList(List<T> originList, int fromIndex, int endIndex) {
        if (CollectionUtils.isEmpty(originList) || fromIndex < 0 || endIndex >= originList.size()
            || fromIndex > endIndex) {
            return Collections.emptyList();
        }
        List<T> list = Lists.newLinkedList();
        for (int i = fromIndex; i <= endIndex; i++) {
            list.add(originList.get(i));
        }
        return list;
    }

    /**
     * 从列表中获取指定索引位置的值，使用场景为从列表中取值，避免业务代码处理数组越界和NPE逻辑
     * @param collection 列表集合
     * @param index      索引位置
     * @param <T>        泛行参数
     * @return 返回值
     */
    public static <T> Optional<T> getValueByIndex(Collection<T> collection, int index) {
        if (Objects.isNull(collection) || collection.isEmpty()) {
            return Optional.empty();
        }
        int len = collection.size();
        if (index < 0 || index >= len) {
            return Optional.empty();
        }
        int pos = 0;
        T v = null;
        for (T value : collection) {
            if (pos == index) {
                v = value;
                break;
            }
            pos = pos + 1;
        }
        return Optional.ofNullable(v);
    }

}